package sise.world.agent;

import java.awt.Point;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;

import sise.world.map.Field;
import CLIPSJNI.Environment;
import CLIPSJNI.MultifieldValue;

public abstract class AbstractAgent {

	protected static final Logger logger = Logger.getLogger(AbstractAgent.class);

	protected AgentType agentType;
	protected Environment clips;
	protected Field ownField;
	protected NextMove nextMove;
	protected int foodQuantity;
	//protected int boostTime;
	protected int hp;
	protected String clipsFilePath;
	protected int livedRoundsQuantity;
	protected int score;
	protected Point nextActionParameters;

	public enum NextMove {
		// for all
		NOTHING,
		MOVE,
		//ACTIVATE_BOOST,

		//BYSTRETKA_ROSLINOZERNA
		COLLECT_FOOD,
		CUT_TREE,
		
		//BYSTRETKA_MIESOZERNA
		COLLECT_WORM,

		// BYSTRETKA_MIESOZERNA and DRAPIEZNIK
		KILL;
	}

	public enum AgentType {
		BYSTRETKA_ROSLINOZERNA("/img/bystretkaR.jpg"), //dawniej digger
		BYSTRETKA_MIESOZERNA("/img/bystretka.jpg"),//dawniej guard
		DRAPIEZNIK("/img/drapieznik.jpg");//dawniej local
		

		private String iconFilePath;

		private AgentType(String iconFilePath) {
			this.iconFilePath = iconFilePath;
		}

		public String getIconFilePath() {
			return iconFilePath;
		}

		public void setIconFilePath(String iconFilePath) {
			this.iconFilePath = iconFilePath;
		}

	}

	protected AbstractAgent(AgentType agentType) {
		super();
		this.agentType = agentType;
		this.foodQuantity = 5;
		this.nextMove = NextMove.NOTHING;
		this.hp = 2;
		this.livedRoundsQuantity = 0;
		this.score = 0;
		this.nextActionParameters = new Point(0, 0);
	}

	public boolean isAlive() {
		return hp > 0 && foodQuantity > 0;
	}

	public String getToolTip() {
		StringBuilder toolTipBuilder = new StringBuilder();
		toolTipBuilder.append("Agent type: <b>");
		toolTipBuilder.append(agentType.name());
		toolTipBuilder.append("</b>");
		toolTipBuilder.append("<br />Logic: <b>");
		toolTipBuilder.append(clipsFilePath);
		toolTipBuilder.append("</b>");
		toolTipBuilder.append("<br />Food: <b><font>");
		toolTipBuilder.append(foodQuantity);
		toolTipBuilder.append("</b></font>");
		toolTipBuilder.append("<br />Score: <b><font color=\"YELLOW\">");
		toolTipBuilder.append(score);
		toolTipBuilder.append("</b></font>");
		toolTipBuilder.append("</b></font>");
		toolTipBuilder.append("<br />Hit points: <b><font color=\"RED\">");
		toolTipBuilder.append(hp);
		toolTipBuilder.append("</b></font>");
		toolTipBuilder.append("<br />Lived rounds: <b><font color=\"GREEN\">");
		toolTipBuilder.append(livedRoundsQuantity);
		toolTipBuilder.append("</b></font>");
		return toolTipBuilder.toString();
	}

	public AgentType getAgentType() {
		return agentType;
	}

	public void setAgentType(AgentType agentType) {
		this.agentType = agentType;
	}

	public Field getOwnField() {
		return ownField;
	}

	public void setOwnField(Field ownField) {
		this.ownField = ownField;
	}

	public NextMove getNextMove() {
		return nextMove;
	}

	public void setNextMove(NextMove nextMove) {
		this.nextMove = nextMove;
	}

	public int getFoodQuantity() {
		return foodQuantity;
	}

	public void setFoodQuantity(int foodQuantity) {
		this.foodQuantity = foodQuantity;
	}

	public void initClips(String rulePath) {
		clipsFilePath = rulePath;
		String resourcePath = "/src/main/resources/";
		String path = "";
		String templatesPath = "rules/templates.clp";
		try {
			path = new java.io.File(".").getCanonicalPath();
		} catch (IOException e) {
			logger.error("Cannot create path to CLIPS file", e);
		}
		clips = new Environment();
		// System.out.println(path + resourcePath + rulePath);
		clips.load(path + resourcePath + templatesPath);
		clips.load(path + resourcePath + rulePath);
	}

	public void processNextMove(InfoToAgent info) {
		Iterator iterator = info.returnObserved().entrySet().iterator();
		System.out.println(agentType);
		clips.reset();
		String ret = "";

		String hpAssert = "(assert (stat (Name hp) (Value " + this.getHp() + ")))";
		String scoreAssert = "(assert (stat (Name score) (Value " + this.getScore() + ")))";
		String foodAssert = "(assert (stat (Name food) (Value " + this.getFoodQuantity() + ")))";

		clips.eval(foodAssert);
		clips.eval(hpAssert);
		clips.eval(scoreAssert);
		int offsetX = this.getOwnField().getCoordinates().x;
		int offsetY = this.getOwnField().getCoordinates().y;
		while (iterator.hasNext()) {
			Map.Entry<Point, AgentType> pair = (Map.Entry) iterator.next();
			String eval = "(assert (visible (Object " + pair.getValue().name() + ") (PosX "
					+ (pair.getKey().y - offsetY) + ") (PosY " + (pair.getKey().x - offsetX) + ")))";
			clips.eval(eval);

		}
		// Przekazanie danych na temat pol
		Iterator iterator2 = info.returnFields().entrySet().iterator();
		String ret2 = "";
		while (iterator2.hasNext()) {
			Map.Entry<Point, Field> pair = (Map.Entry) iterator2.next();
			String eval = "(assert (field (Type " + pair.getValue().getFieldType().toString() + ") (Resource "
					+ pair.getValue().getResources() + ") (PosX " + (pair.getKey().y - offsetY) + ") (PosY "
					+ (pair.getKey().x - offsetX) + ")))";
			clips.eval(eval);
		}

		clips.run();
		// System.out.println(clips.eval("(facts)"));

		int retX = 0;
		int retY = 0;
		try {
			MultifieldValue mv = (MultifieldValue) clips.eval("(find-all-facts ((?m move)) TRUE)");
			ret = (mv.get(0).getFactSlot("Move").getValue().toString());
			retX = (int) Double.parseDouble((mv.get(0)).getFactSlot("MoveX").getValue().toString());
			retY = (int) Double.parseDouble((mv.get(0)).getFactSlot("MoveY").getValue().toString());
			Point nextAction = new Point(retX, retY);
			this.setNextActionParameters(nextAction);
		} catch (Exception e) {

		}
		// Wybór ruchu na podstawie odpowiedzi CLIPSA
		switch (ret) {
		case "move":
			this.setNextMove(NextMove.MOVE);
			this.nextActionParameters.x = retX;
			this.nextActionParameters.y = retY;
			break;
		case "collectFood":
			this.setNextMove(NextMove.COLLECT_FOOD);
			break;
		case "collectWorm":
			this.setNextMove(NextMove.COLLECT_WORM);
			break;
		case "kill":
			this.setNextMove(NextMove.KILL);
			this.nextActionParameters.x = retX;
			this.nextActionParameters.y = retY;
			break;
		case "nothing":
			System.out.println("kupa");
			this.setNextMove(NextMove.NOTHING);
			break;
		case "cutTree":
			this.setNextMove(NextMove.CUT_TREE);
			this.nextActionParameters.x = retX;
			this.nextActionParameters.y = retY;
			break;
		}
	}

	public String getClipsFilePath() {
		return clipsFilePath;
	}

	public int getHp() {
		return hp;
	}

	public void setHp(int hp) {
		this.hp = hp;
	}

	public int getScore() {
		return score;
	}

	public void setScore(int score) {
		this.score = score;
	}

	public int getLivedRoundsQuantity() {
		return livedRoundsQuantity;
	}

	public void setLivedRoundsQuantity(int livedRoundsQuantity) {
		this.livedRoundsQuantity = livedRoundsQuantity;
	}

	public Point getNextActionParameters() {
		return nextActionParameters;
	}

	public void setNextActionParameters(Point nextActionParameters) {
		this.nextActionParameters = nextActionParameters;
	}

	public void survived() {
		this.livedRoundsQuantity++;
	}

	public void dealDamage(int damage) {
		this.hp -= damage;
	}

	public void addScore(int score) {
		this.score += score;
	}
}
